import { popTarget, pushTarget } from "./dep";

let uid = 0;
export default class Watcher {
  constructor(vm, exprOrFn, cb, options, isRenderWatcher) {
    this.id = uid++;
    this.deps = [];
    this.vm = vm;
    this.isRenderWatcher = isRenderWatcher;
    if (options) {
      this.lazy = options.lazy; // 计算属性watcher标识
      this.user = options.user //配置项自定义的watcher标识
    }
    if(exprOrFn) { //自定义watcher 参数
      if(typeof exprOrFn === 'string') {
        this.getter = function() {
          return vm[exprOrFn] //取值
        } 
      } else {
        this.getter = exprOrFn
      }
    }
    this.cb = cb;//需要执行的事件，例如自定义watcher
    this.depsIds = new Set();
    this.dirty = this.lazy;
    this.value = this.lazy ? undefined : this.get(); //计算属性默认不执行
  }
  addDep(dep) {
    //收集dep
    const id = dep.id;
    if (!this.depsIds.has(id)) {
      this.depsIds.add(id);
      this.deps.push(dep);
      dep.addSub(this);
    }
  }
  get() {
    //开始学习使用这个一步一步来递进
    // Dep.target = this;
    // this.getter();
    // Dep.target = null;
    //计算属性
    pushTarget(this);
    const value = this.getter.call(this.vm);
    popTarget();
    return value;
  }
  update() {
    // this.getter();
    if (this.lazy) {//计算属性watcher标识
      this.dirty = true;
      console.log("修改了dirty = true");
      
    } else {
      console.count("进入watcher队列并执行次数");
      queueWatcher(this);
    }
  }
  depend() {
    //计算属性上添加 渲染watcher
    let i = this.deps.length;
    while (i--) {
      this.deps[i].depend() //dep收集watcher的同时，watcher也要双向收集dep，所以调用这个方法
    }
  }
  evaluate() {
    this.value = this.get();
    this.dirty = false;
  }
  run() {
    const oldValue = this.value
    const newValue = this.get();
    if(this.user) {
      this.cb.call(this.vm, newValue, oldValue)
    }
  }
}

/**
 * 异步更新
 */
let queue = [];
let has = {};
let flushing = false;
function queueWatcher(watcher) {
  const id = watcher.id;
  if (has[id]) return;
  queue.push(watcher);
  has[id] = true;
  if (!flushing) {
    nextTick(flushSchedulerQueue);
    flushing = true;
  }
}

//依次执行队列
function flushSchedulerQueue() {
  const queueList = queue.slice(0);
  queue = [];
  flushing = false;
  has = {};
  queueList.forEach((q) => q.run());
}

// nextTick原理
let callbacks = [];
let padding = false;

//优雅降级
let timerFunc;
if (typeof Promise !== "undefined") {
  timerFunc = () => {
    Promise.resolve().then(flushCallbacks);
  };
} else if (typeof MutationObserver !== "undefined") {
  const observer = new MutationObserver(flushCallbacks);
  const textNode = document.createTextNode(1);
  observer.observe(textNode, { characterData: true });
  timerFunc = () => {
    textNode = 2;
  };
} else if (typeof setImmediate !== "undefined") {
  setImmediate(flushCallbacks);
} else {
  timerFunc = () => {
    setTimeout(flushCallbacks, 0);
  };
}

function nextTick(cb) {
  if (padding) return;
  callbacks.push(cb);
  timerFunc();
  padding = true;
}

function flushCallbacks() {
  padding = false;
  callbacks.forEach((cb) => cb());
}

